/*
 * Copyright (c) 2007-2011, Petr Panteleyev <petr@panteleyev.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.gclient;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Date;
import java.util.EventObject;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingWorker;
import javax.swing.text.JTextComponent;
import javax.swing.text.html.HTML;

import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXStatusBar;
import org.panteleyev.blogclient.BlogPostEditorFrame;
import org.panteleyev.blogclient.DateTimePicker;
import org.panteleyev.blogclient.FontManager;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.Options;
import org.panteleyev.blogclient.PreviewFrame;
import org.panteleyev.blogclient.Previewer;
import org.panteleyev.blogclient.TagListManager;
import org.panteleyev.blogclient.UI;
import org.panteleyev.blogclient.events.TemplatesChangedEvent;
import org.panteleyev.html.Editor;
import org.panteleyev.html.PlainEditor;
import org.panteleyev.swing.CollapseButton;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.utilities.events.AppEventListener;

/**
 *
 * @author Petr Panteleyev
 */
public class GBEditorFrame extends BlogPostEditorFrame<GBUserProfile,GBEntry> {
    private AbstractAction openTagsListAction;
    private JTextField labelsEdit;
    private DateTimePicker datePicker = new DateTimePicker();
    private JButton labelsButton;
    private JCheckBox manualDateCheck;
    private JXCollapsiblePane collPane = new JXCollapsiblePane();
    private JCheckBox draftCheckBox;
    private JLabel fileNameField = new JLabel("-");
    private JTextPane _plainEditor = new JTextPane();

    private final class PostingThread extends SwingWorker<Object,Object> {
        private Exception t;
        private boolean close;

        PostingThread(boolean close) {
            this.close = close;
            getBusyLabel().setBusy(true);
            getPublishAction().setEnabled(false);
            getPublishAndContinueAction().setEnabled(false);
        }

        @Override
        protected Object doInBackground() throws Exception {
            try {
                if (_post.getId() == null) {
                    _account.createEntry(_post);
                    // TODO: reload window fields
                } else {
                    _account.updateEntry(_post);
                }
            } catch (Exception e) {
                t = e;
            }

            return null;
        }

        @Override
        protected void done() {
            if (t != null) {
                handleException(t);
            } else {
                if (close) {
                    afterPostSent();
                }
            }

            getBusyLabel().setBusy(false);
            getPublishAction().setEnabled(true);
            getPublishAndContinueAction().setEnabled(true);
        }
    }

    private static String ALBUM_REFERENCE = "<table style=\"width:194px;\"><tr><td align=\"center\" style=\"height:194px;background:url(http://picasaweb.google.com/f/img/transparent_album_background.gif) no-repeat left\"><a href=\"%s\"><img src=\"%s\" width=\"160\" height=\"160\" style=\"margin:1px 0 0 4px;\"></a></td></tr><tr><td style=\"text-align:center;font-family:arial,sans-serif;font-size:11px\"><a href=\"%s\" style=\"color:#4D4D4D;font-weight:bold;text-decoration:none;\">%s</a></td></tr></table>";

    /* Actions */
    private AbstractAction          editInsertPicasaAlbumReferenceAction;
    private AbstractAction          uploadPhotoAction;

    private GBAccount               _account;
    private GBEntry                 _post;
    private DefaultComboBoxModel    _blogsComboBoxModel;
    private JTextComponent          _focusedText;
    private JMenu                   _labelsMenu = new JMenu();

    private PlainEditor             plainEditor = new PlainEditor();
    private Editor                  editor = plainEditor;

    private TagListManager          tagListManager;


    private void constructor(GBAccount account, GBEntry post) {
        _account = account;
        _post = post;

        createActions();
        initComponents();
        createMenuBar();

        _plainEditor.setFont(FontManager.getEditorFont());

        tagListManager = new TagListManager(labelsEdit);

        datePicker.setEnabled(false);

        plainEditor.setEditor(_plainEditor);
        plainEditor.newDocument();
        editor = plainEditor;

        _blogsComboBoxModel = new DefaultComboBoxModel() {
            private ArrayList<GBlog> blogs = _account.getBlogs();

            @Override
            public int getSize() { return blogs.size(); }
            @Override
            public Object getElementAt(int index) {
                return blogs.get(index).getTitle();
            }
        };

        blogsComboBox.setModel(_blogsComboBoxModel);

        if (getProfile().isOnline() && account.hasBlogs()) {
            _labelsMenu = tagListManager.buildTagsMenu(_account.getBlog(0).getLabels());
        }

        /* Fill fields */
        if (_post != null) {
            subjectEdit.setText(_post.getSubject());
            _plainEditor.setText(_post.getBody());
            datePicker.setDate(_post.getDate());

            if (_post.getBlog() == null) {
                if (getProfile().isOnline()) {
                    blogsComboBox.setSelectedIndex(0);
                }
            } else {
                blogsComboBox.setSelectedIndex(_account.getBlogIndex(_post.getBlog()));
                blogsComboBox.setEnabled(false);
            }

            /* Labels */
            ArrayList<String> labels = _post.getLabels();
            StringBuilder b = new StringBuilder();
            for (int i = 0, labelCount = labels.size(); i < labelCount; i++) {
                b.append(labels.get(i));
                if (i != labelCount - 1) {
                    b.append(", ");
                }
            }
            labelsEdit.setText(b.toString());

            manualDateCheck.setSelected(true);
            datePicker.setEnabled(true);
            draftCheckBox.setSelected(_post.isDraft());
            setTitle();
        } else {
            setEmptyPost();
        }

        /* Setup undo/redo */
        updateUndoRedo(false, false);
        getTextUndoManager().addComponent(_plainEditor);
        getTextUndoManager().addComponent(subjectEdit);
        getTextUndoManager().addComponent(labelsEdit);

        getPublishLaterAction().setEnabled(false);
        blogsComboBox.setVisible(getProfile().isOnline());

        addAppEventListener(TemplatesChangedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                _plainEditor.setComponentPopupMenu(generateEditorPopupMenu());
            }
        });


        Options.getBounds(this);
    }

    private void initComponents() {
        // Toolbar panel
        getContentPane().add(
            SwingFactory.createToolBarPanel(
                createProfileToolBar(false, true),
                Box.createHorizontalGlue(),
                getBusyLabel(),
                Box.createHorizontalStrut(5)
            ), BorderLayout.NORTH
        );

        JPanel centerPanel = new JPanel(new BorderLayout());

        // Subject
        centerPanel.add(SwingFactory.createPanel(BorderFactory.createEmptyBorder(5, 5, 5, 5), SwingFactory.LAYOUT.BOX_X,
            new JLabel(getPostWindowSubjectLabelText()),
            Box.createHorizontalStrut(3),
            subjectEdit = new JTextField(),
            Box.createHorizontalStrut(5),
            blogsComboBox = new JComboBox()
        ), BorderLayout.NORTH);

        /* Subject */
        subjectEdit.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                _focusedText = subjectEdit;
            }
            @Override
            public void focusLost(java.awt.event.FocusEvent evt) {
                setTitle();
            }
        });

        /* Blog chooser */
        blogsComboBox.setFocusable(false);
        blogsComboBox.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                int index = blogsComboBox.getSelectedIndex();
                if (index != -1) {
                    _labelsMenu = tagListManager.buildTagsMenu(_account.getBlog(index).getLabels());
                }
            }
        });

        // Editor
        centerPanel.add(new JScrollPane(_plainEditor), BorderLayout.CENTER);
        _plainEditor.setComponentPopupMenu(generateEditorPopupMenu());
        _plainEditor.setMargin(UI.EDITOR_MARGIN);

        JPanel optionsPanelTop = SwingFactory.createPanel(
            BorderFactory.createEmptyBorder(0, 5, 5, 5),
            SwingFactory.LAYOUT.BOX_X,
            new JLabel(L10N.POST_WINDOW_LABELS_LABEL.s()),
            Box.createHorizontalStrut(3),
            labelsEdit = new JTextField(),
            Box.createHorizontalStrut(3),
            labelsButton = new JButton(openTagsListAction),
            Box.createHorizontalStrut(5),
            new JSeparator(JSeparator.VERTICAL),
            Box.createHorizontalStrut(5),
            manualDateCheck = new JCheckBox(new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    datePicker.setEnabled(manualDateCheck.isSelected());
                }
            }),
            Box.createHorizontalStrut(3),
            datePicker
        );

        JPanel optionsPanel = SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_X,
            draftCheckBox = new JCheckBox(getPostWindowDraftCheckBoxText()),
            Box.createHorizontalGlue()
        );

        collPane.setLayout(new BoxLayout(collPane.getContentPane(), BoxLayout.Y_AXIS));
        SwingFactory.add(collPane,
            new JSeparator(),
            optionsPanel
        );
        collPane.setCollapsed(true);

        JButton tButton = new CollapseButton(collPane);
        tButton.setFocusable(false);

        JPanel optionsPanelMain = SwingFactory.createPanel(BorderFactory.createEmptyBorder(5, 0, 0, 5), SwingFactory.LAYOUT.BOX_X,
            SwingFactory.createPanel(null, SwingFactory.LAYOUT.BOX_Y,
                optionsPanelTop,
                collPane
            ),
            tButton
        );

        SwingFactory.setAlignmentY(optionsPanelMain, TOP_ALIGNMENT);
        centerPanel.add(optionsPanelMain, BorderLayout.SOUTH);

        getContentPane().add(centerPanel, BorderLayout.CENTER);

        // Status bar
        JXStatusBar stBar = new JXStatusBar();
        stBar.add(fileNameField, new JXStatusBar.Constraint(JXStatusBar.Constraint.ResizeBehavior.FILL));
        getContentPane().add(stBar, BorderLayout.SOUTH);
    }

    @Override
    public void setVisible(boolean b) {
        super.setVisible(b);
        _plainEditor.requestFocus();
    }

    @Override
    protected void setEmptyPost() {
        _post = null;
        subjectEdit.setText("");
        _plainEditor.setText("");
        if (getProfile().isOnline() && getAccount().hasBlogs()) {
            blogsComboBox.setSelectedIndex(0);
        }
        labelsEdit.setText("");
        manualDateCheck.setSelected(false);
        datePicker.setEnabled(false);
        setTitle();
    }

    /** Creates new form GBEditorFrame
     * @param profile
     * @param post
     */
    public GBEditorFrame(GBUserProfile profile, GBEntry post) {
        super(profile);
        constructor(profile.getAccount(), post);
    }

    @Override
    public GBEntry getPost() { return _post; }
    public String getPostID() { return (_post == null)? null : _post.getId(); }
    public GBAccount getAccount() { return _account; }

    private void createActions() {
        openTagsListAction = new AbstractAction(null, Icon.FORWARD.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onOpenTagsList();
            }
        };

        editInsertPicasaAlbumReferenceAction = new AbstractAction(L10N.INSERT_PICASA_ALBUM_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) { onInsertPicasaAlbumReference(); }
        };

        uploadPhotoAction = new AbstractAction(L10N.UPLOAD_PHOTO_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) { onUploadPhoto(); }
        };

        editInsertPicasaAlbumReferenceAction.setEnabled(getProfile().isOnline());
        uploadPhotoAction.setEnabled(getProfile().isOnline());
    }

    private void createMenuBar() {
        createMenuBar(
            createFileMenu(),
            createEditMenu(
                getUndoAction(),
                getRedoAction(),
                null,
                getCutAction(),
                getCopyAction(),
                getPasteAction(),
                null,
                getEditInsertReferenceAction(),
                getEditInsertImageAction(),
    //        insertAdvUserReferenceAction),
                null,
                editInsertPicasaAlbumReferenceAction,
                uploadPhotoAction,
                null,
                generateQuotesSubmenu(),
                generateSymbolsSubmenu(),
                getTemplateMenu(),
                null,
                getEditTypografAction()
            ),
            generateFormatMenu(),
            createEntryMenu(),
            createProfileMenu(),
            createToolsMenu(),
            createWindowMenu(),
            createHelpMenu()
        );

        getDeleteEntryAction().setEnabled(false);
    }

    @Override
    protected GBEntry createPostObject() {
        int index = blogsComboBox.getSelectedIndex();
        if (index == -1) {
            return null;
        }

        GBlog blog = _account.getBlogs().get(index);
        GBEntry post = new GBEntry(blog);

        setEntryObject(post);
        return post;
    }

    private void setEntryObject(GBEntry entry) {
        entry.setSubject(subjectEdit.getText());
        entry.setBody(editor.getTextForPost());

        if (manualDateCheck.isSelected()) {
            entry.setDate(datePicker.getDate());
        } else {
            entry.setDate(new Date());
        }

        /* Draft */
        entry.setDraft(draftCheckBox.isSelected());

        /* Tags */
        ArrayList<String> labels = tagListManager.getTags();
        if (labels != null) {
            entry.setLabels(labels);
        }

//        Person author = new Person("Petr Panteleyev", null, "petr.panteleyev@gmail.com");
//        newEntry.getAuthors().add(author);
    }

    private void onPost(boolean close) {
        int index = blogsComboBox.getSelectedIndex();
        if (index == -1) {
            return;
        }

        GBlog blog = _account.getBlogs().get(index);
        if (_post == null) {
            _post = new GBEntry(blog);
        } else {
            _post.setBlog(blog);
        }

        setEntryObject(_post);

        new PostingThread(close).execute();
    }

    private void setTitle() {
        StringBuilder b = new StringBuilder();

        final String subjText = subjectEdit.getText();
        if (subjText.length() != 0) {
            b.append(subjText);
        } else {
            final String t = (_post == null)? getPostWindowNewPostTitle() : getPostWindowEditPostTitle();
            b.append(t);
        }

        setTitle(b.toString());
    }

    @Override
    public void insertText(String text) {
        if (_focusedText != null) {
            _focusedText.replaceSelection(text);
            _focusedText.requestFocus();
        }
    }

    @Override
    public String getSelectedText() {
        return editor.getSelectedText();
    }

    public void onInsertPicasaAlbumReference() {
        GBInsertAlbumDialog dial = new GBInsertAlbumDialog(this, _account);
        if (dial.showDialog()) {
            int index = dial.getSelectedIndex();
            String t = dial.getAlbumTitle();
            GAlbum album = _account.getAlbum(index);
            if (album != null) {
                String html = album.getHtmlLinkAsString();
                String refText = String.format(ALBUM_REFERENCE, html, album.getThumbnailUrl(), html, t);

                getEditor().insertHTML(refText, HTML.Tag.TABLE);
            }
        }
    }

    @Override
    public Editor getEditor() {
        return editor;
    }

    public void onUploadPhoto() {
        GBPostPictureDialog dial = new GBPostPictureDialog(this, getProfile());
        if (dial.showDialog()) {
            GPhoto photo = dial.getPhoto();

            StringBuilder b = new StringBuilder();

            if (dial.getLinkToSource()) {
                b.append("<a href='").append(photo.getHtmlLinkAsString()).append("'>");
            }

            b.append("<img src='");
            if (dial.getFullSize()) {
                b.append(photo.getRawImageUrl());
            } else {
                int sizeIndex = dial.getSmallSizeIndex();
                String thUrl = photo.getThumbnail(sizeIndex).getUrl();
                b.append(thUrl);
            }
            b.append("' border='");

            if (dial.getImageBorder()) {
                b.append("1'");
            } else {
                b.append("0'");
            }

            if (dial.getFullSize()) {
                b.append(" width='")
                    .append(photo.getWidth().toString())
                    .append("'")
                    .append(" height='")
                    .append(photo.getHeight().toString())
                    .append("'");
            }

            b.append(">");

            if (dial.getLinkToSource()) {
                b.append("</a>");
            }

            HTML.Tag tag = (dial.getLinkToSource())? HTML.Tag.A : HTML.Tag.IMG;
            getEditor().insertHTML(b.toString(), tag);
        }
    }

    private JTextField              subjectEdit;
    private JComboBox               blogsComboBox;
//    private JCheckBox               manualDateCheck;
//    private JTextField              labelsEdit;
//    private JCheckBox               draftCheckBox;

    @Override
    public void onPublish() {
        onPost(true);
    }

    @Override
    public void onPublishAndContinue() {
        onPost(false);
    }

    @Override
    public void onPublishLater() {

    }

    @Override
    public void onPreviewEntry() {
        int index = blogsComboBox.getSelectedIndex();
        if ((index == -1) && getProfile().isOnline()) {
            return;
        }

        GBEntry post = null;

        if (index == -1) {
            post = new GBEntry();
        } else {
            GBlog blog = _account.getBlogs().get(index);
            post = new GBEntry(blog);
        }

        post.setSubject(subjectEdit.getText());
        post.setBody(editor.getTextForPreview(getProfile().isConvertLineBreaks()));
        if (datePicker.isEnabled()) {
            post.setDate(datePicker.getDate());
        } else {
            post.setDate(new Date());
        }

        try {
            Previewer previewer = new Previewer(post);

            if (Options.getUseExternalPreview()) {
                OSUtil.runBrowser(previewer.getFullFileURL().toString());
            } else {
                PreviewFrame<?> pFrame = PreviewFrame.findPreviewFrame(PreviewFrame.class, this);
                if (pFrame == null) {
                    pFrame = new PreviewFrame<>(getProfile(), this);
                }
                pFrame.setContent(null, previewer.getFullFileURL());
                pFrame.setVisible(true);
            }
        } catch (Exception e) {
            handleException(e);
        }
    }

    @Override
    public void onDeleteEntry() {
    }

    private void onOpenTagsList() {
        if (_labelsMenu != null) {
            int x = labelsButton.getX();
            int y = labelsButton.getY();
            _labelsMenu.getPopupMenu().show(labelsButton.getParent(), x, y);
        }
    }

    @Override
    public void onUpdateTags() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}
